import time

from logger import get_logger
from config import config
from process import Process
from utils import OrderQueue
from detect.detect import CLASSES
from .object_tracer import ObjectTracer
from .triggers import *


class AlertExecutor:

    def __init__(self):
        self._tracking_objs = None
        self._logger = get_logger('alert')

        self._enter_triggers = None
        self._leave_triggers = None
        self._update_triggers = None
        
        if alert_cfg := config.alert:
            self._tracking_objs = set()
            self._enter_triggers: dict[str, list[AlertTrigger]] = {}
            self._leave_triggers: dict[str, list[AlertTrigger]] = {}
            self._update_triggers: dict[str, list[AlertTrigger]] = {}
            # 下面的循环就是为了处理成上面这种格式
            for alert in alert_cfg:
                obj: str = alert['object']
                if obj not in CLASSES:
                    self._logger.warning(f'物体[{obj}]不在该模型可检测物体的列表中，将忽略事件[{alert["event"]}]...')
                    continue
                self._tracking_objs.add(obj)
                try:
                    trigger = parse_trigger(alert)
                except Exception as e:
                    self._logger.warning(f'事件[{alert["event"]}]添加失败：{str(e)}')
                    continue
                for method, triggers in (
                    ('enter', self._enter_triggers),
                    ('leave', self._leave_triggers),
                    ('update', self._update_triggers),
                ):
                    if not hasattr(trigger, method):
                        continue
                    if obj in triggers:
                        triggers[obj].append(trigger)
                    else:
                        triggers[obj] = [trigger]

            # 处理成{类别1: 空集合, 类别2: 空集合...}的格式
            self._tracking_objs = {obj: set() for obj in self._tracking_objs}
            self._exist_objs = {obj: set() for obj in self._tracking_objs}

            if not self._tracking_objs:
                self._tracking_objs = self._exist_objs = None
            else:
                self._logger.info(f'检测到有效报警事件，将对物体:{list(self._tracking_objs.keys())}进行目标追踪')
        if not self._tracking_objs:
            self._logger.info('没有有效的报警事件，将不对任何物体执行跟踪算法')


    def update(self, image, results):
        if not self._tracking_objs:
            return
        current_time = time.perf_counter()

        # 获取所有正在追踪的物体，之后排查谁没出现时方便
        notfound_objs = set().union(*self._tracking_objs.values())
        obj_and_action = []

        def process_action(action, obj):
            # 存储动作等待后续处理
            if action == ObjectTracer.Action.ENTER or action == ObjectTracer.Action.LEAVE:
                obj_and_action.append((action, obj))
            # 处理物体集合
            if action == ObjectTracer.Action.ENTER:
                self._exist_objs[obj.label].add(obj)
            elif action == ObjectTracer.Action.LEAVE:
                self._exist_objs[obj.label].discard(obj)
                self._tracking_objs[obj.label].discard(obj)
            elif action == ObjectTracer.Action.FAKE:
                self._tracking_objs[obj.label].discard(obj)

        for box, label_idx, _ in results:
            # 找到对应的类型
            label = CLASSES[label_idx]

            if (obj_set := self._tracking_objs.get(label)) is None:
                continue

            max_sim = 0
            max_sim_obj = None
            for obj in obj_set:
                # 如果这个物体被认领了就直接跳过
                if obj not in notfound_objs:
                    continue
                sim = obj.sim(box)
                # 找最大位置相似度物体，并且必须大于最小位置相似度
                if sim > max_sim and sim > ObjectTracer.TRACE_MIN_SIM:
                    max_sim = sim
                    max_sim_obj = obj
            if max_sim_obj is not None:
                # 可以找到之前追踪的物体
                notfound_objs.discard(max_sim_obj)
                action = max_sim_obj.update(current_time, box, image)
                # 判断是否是进入或者离开的物体
                process_action(action, max_sim_obj)
            else:
                # 那就新建一个物体追踪器
                obj_set.add(ObjectTracer(label, box, image))
        
        # 没有被认领的物体空更新一次
        for obj in notfound_objs:
            process_action(obj.update(current_time), obj)
        
        # 到这里，所有的集合都更新完毕，下面进行触发器的更新
        for action, obj in obj_and_action:
            label = obj.label
            # 判断是否是进入或者离开的物体
            if action == ObjectTracer.Action.ENTER:
                if triggers:= self._enter_triggers.get(label):
                    for trigger in triggers:
                        trigger.enter(obj, self._exist_objs[label], image)
            elif action == ObjectTracer.Action.LEAVE:
                if triggers:= self._leave_triggers.get(label):
                    for trigger in triggers:
                        trigger.leave(obj, self._exist_objs[label], image)
        for label, triggers in self._update_triggers.items():
            for trigger in triggers:
                trigger.update(self._exist_objs[label], image)

class Alerter(Process):

    def __init__(self, config, alert_queue):
        super().__init__()
        self._add_process(config, alert_queue)
        self._start()

    def _run(self, config, alert_queue):
        alerter = AlertExecutor()
        if config.multiprocess:
            queue = OrderQueue(alert_queue)
        else:
            queue = alert_queue
        del config
    
        while True:
            # results大概长这样：[(<int x1>, <int y1>, <int x2>, <int y2>), <int 类别编号>, <float 置信度>), ...]
            _, frame, results = queue.get()
            if frame is not None and results is not None:
                alerter.update(frame, results)  # frame是opencv的图像，因为报警时需要个base64截图，所以图像也传过来了
